#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <unordered_map>

#include "Sock.hpp"
#include "log.hpp"
#include "Epoll.hpp"
#include "Protocol.hpp"

// sock必须要有自己独立的接收缓冲区和发送缓冲区

class TcpServer;
class Connection;

using func_t = std::function<void(Connection*)>; // 此处的using意为typedef
using callback_t = std::function<void(Connection*, std::string&)>;

class Connection
{
public:
    Connection(int sock = -1)
        : _sock(sock)
        , _tsvr(nullptr)
    {}
    void SetCallBack(func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }
    ~Connection()
    {}
public:
    int _sock;         // 负责进行IO的文件描述符

    func_t _recv_cb;   // 读事件回调函数 
    func_t _send_cb;   // 写事件回调函数
    func_t _except_cb; // 异常事件回调函数

    std::string _inbuffer; // 接收缓冲区
    std::string _outbuffer; // 发送缓冲区

    TcpServer* _tsvr; // 回值指针，指向原来的TcpServer
};

class TcpServer
{
    const static int gnum = 128;
public:
    TcpServer(int port = 8080)
        : _port(port)
        , _revs_num(gnum)
    {
        // 创建套接字
        _listensock = Sock::Socket();
        Sock::Bind(_listensock, _port);
        Sock::Listen(_listensock);

        // 创建epoll模型
        _poll.CreateEpoll();

        // 我们要将所有的sock都添加到epoll模型中，所以要管理所有的sock，使用unordered_map
        AddConnection(_listensock, std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
    
        // 构建获取事件的缓冲区
        _revs = new struct epoll_event[_revs_num];
    }

    // 将任意sock添加到TcpServer
    void AddConnection(int sock, func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        // 需要循环读取，所以设置sock为非阻塞
        Sock::SetNonBlock(sock);
        // 实例化sock对象
        Connection* conn = new Connection(sock);
        conn->SetCallBack(recv_cb, send_cb, except_cb);
        conn->_tsvr = this;

        // 将sock添加到epoll
        _poll.AddSockToEpoll(sock, EPOLLIN | EPOLLET); // 多路转接的服务器，一般只打开读事件的关心，写事件按需打开

        // 将sock添加到map中
        _connections.insert(std::make_pair(sock, conn));
    }

    void Accepter(Connection* conn)
    {
        // listensock已经就绪
        while (true)
        {
            std::string clientip;
            uint16_t clientport;
            int accept_errno = 0;
            int sock = Sock::Accept(conn->_sock, &clientip, &clientport, &accept_errno);
            if (sock < 0)
            {
                if (accept_errno == EAGAIN || accept_errno == EWOULDBLOCK) break;
                else if (accept_errno == EINTR) continue;
                else
                {
                    // 失败
                    logMessage(WARNING, "accept, error: %d : %s", accept_errno, strerror(accept_errno));
                    break;
                }

            }
            // 将sock交给TcpServer处理
            AddConnection(sock, std::bind(&TcpServer::Recver, this, std::placeholders::_1),\
                                std::bind(&TcpServer::Sender, this, std::placeholders::_1),\
                                std::bind(&TcpServer::Excepter, this, std::placeholders::_1));
            logMessage(DEBUG, "accept client[sock: %d] %s:%d success, add to epoll&&TcpServer", sock, clientip.c_str(), clientport);
        }
    }

    bool EnableReadWrite(Connection* conn, bool readable, bool writeable)
    {
        uint32_t events = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0);
        bool n = _poll.CtlEpoll(conn->_sock, events);
        return n;
    }

    void Recver(Connection* conn)
    {
        const int num = 1024;
        bool err = false;
        while (true)
        {   
            char buffer[num];
            ssize_t n = recv(conn->_sock, buffer, sizeof(buffer) - 1, 0);
            if (n < 0)
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK) break;
                else if (errno == EINTR) continue;
                else
                {
                    logMessage(ERROR, "recv error: %d : %s", errno, strerror(errno));
                    conn->_except_cb(conn);
                    err = true;
                    break;
                }
            }
            else if (n == 0)
            {
                logMessage(DEBUG, "client[%d] quit, me too", conn->_sock);
                conn->_except_cb(conn);
                err = true;
                break;
            }
            else
            {
                buffer[n] = 0;
                conn->_inbuffer += buffer;
            }
        }
        // logMessage(DEBUG, "conn->_inbuffer[sock: %d], %s", conn->_sock, conn->_inbuffer.c_str());
        if (!err)
        {
            std::vector<std::string> message;
            SpliteMessage(conn->_inbuffer, &message);
            for (auto& msg : message)
            {
                // 可以拿到完整的报文
                _cb(conn, msg);
            }
        }
    }

    void Sender(Connection* conn)
    {
        while (true)
        {
            ssize_t n = send(conn->_sock, conn->_outbuffer.c_str(), conn->_outbuffer.size(), 0);
            if (n > 0)
            {
                conn->_outbuffer.erase(0, n);
                if (conn->_outbuffer.empty()) break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK) break;
                else if (errno == EINTR) continue;
                else
                {
                    logMessage(ERROR, "send error: %d : %s", errno, strerror(errno));
                    conn->_except_cb(conn);
                    break;
                }
            }
        }
        if (conn->_outbuffer.empty()) EnableReadWrite(conn, true, false);
        else EnableReadWrite(conn, true, true);
    }

    void Excepter(Connection* conn)
    {
        if (!isConnectionExits(conn->_sock)) return; // 表示该文件描述符已经被处理了
        // 将sock移出epoll
        _poll.DeleteSockFromEpoll(conn->_sock);

        // 从unordered_map中释放
        _connections.erase(conn->_sock);

        // close
        close(conn->_sock);

        // delete conn
        delete conn;

        logMessage(DEBUG, "回收所有的异常情况");
    }

    bool isConnectionExits(int sock)
    {
        return _connections.count(sock) > 0;
    }

    void LoopOnce()
    {
        int n = _poll.WaitEpoll(_revs, _revs_num);
        for (int i = 0; i < n; i++)
        {
            int sock = _revs[i].data.fd;
            uint32_t revents = _revs[i].events;

            // 将所有的异常全部交给read或write来统一处理
            if (revents & EPOLLERR) revents |= (EPOLLIN | EPOLLOUT);
            if (revents & EPOLLHUP) revents |= (EPOLLIN | EPOLLOUT);

            if (revents & EPOLLIN)
            {
                if (isConnectionExits(sock) && _connections[sock]->_recv_cb)
                {
                    // sock存在，并且recv方法被设置
                    _connections[sock]->_recv_cb(_connections[sock]);
                }
            }
            if (revents & EPOLLOUT)
            {
                if (isConnectionExits(sock) && _connections[sock]->_send_cb)
                {
                    _connections[sock]->_send_cb(_connections[sock]);
                }
            }
        }
    }

    void Dispather(callback_t cb)
    {
        _cb = cb;
        while (true)
        {
            LoopOnce();
        }
    }

    ~TcpServer()
    {
        if (_listensock > 0) close(_listensock);
        if (_revs) delete[] _revs;
    }

private:
    int _listensock;
    int _port;
    Epoll _poll;
    std::unordered_map<int, Connection*> _connections; // sock : Connection*
    struct epoll_event *_revs;
    int _revs_num;

    callback_t _cb; // 业务处理
};